Skip to content

feat: recover privacy-minimized clinical feedback#611

Merged
BigSimmo merged 5 commits into
mainfrom
codex/cleanup-domain1-governance
Jul 13, 2026
Merged

feat: recover privacy-minimized clinical feedback#611
BigSimmo merged 5 commits into
mainfrom
codex/cleanup-domain1-governance

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • add privacy-minimized answer feedback using per-response interaction IDs and client-side SHA-256 answer hashes
  • expose the existing source-review lifecycle through an authenticated, rate-limited document review route
  • align generated Supabase types, rate-limit buckets, site-map output, and focused route tests

Why

This recovers the patch-unique application layer from the completed Domain 1 governance branch. The supporting schema, migrations, append-only review RPC, and feedback table are already on main; obsolete migration and production-posture history was intentionally not carried forward.

Areas touched

  • answer and streaming API response contracts
  • ClinicalDashboard answer feedback submission
  • answer-feedback and document-review API routes
  • rate limits, generated database types, shared types, site map, and focused tests

Verification

  • npm exec vitest run tests/answer-feedback-route.test.ts tests/source-review-route.test.ts tests/api-rate-limit-fallback.test.ts tests/private-rag-access.test.ts tests/private-access-routes.test.ts tests/rag-answer-fallback.test.ts — 6 files, 161 tests passed after main refresh
  • npm run typecheck — passed after refreshing the dependency tree for the updated main base
  • npm run test — 213 files passed, 1 skipped; 1,940 tests passed, 1 skipped
  • npm run eval:rag:offline — fixture schema plus 21 files / 265 tests passed
  • npm run build — production webpack build and client-bundle secret scan passed
  • focused Chromium: demo answer flow reaches a source-backed answer @critical — passed
  • format, lint, runtime, GitHub pin, site-map, brand, type-scale, and icon-scale checks passed

Checks not fully completed

  • the full Chromium project was interrupted after slow local-server loading/timeouts in unrelated baseline smoke cases; the focused answer-flow test passed on the identity-verified webpack server
  • npm run check:production-readiness ran locally but reported missing Supabase/OpenAI env configuration in the isolated cleanup worktree; no provider call or live mutation was made
  • no migration replay was run because this PR adds no schema or migration and consumes migrations already merged on main

Production readiness / governance

  • no live schema/data mutation and no new migration
  • feedback persists an answer hash and source/request identifiers, not query or answer text
  • source reviews require authentication and use the existing service-role-only transactional RPC
  • anonymous feedback remains rate-limited and the database enforces one record per interaction ID

Summary by CodeRabbit

  • New Features

    • Added answer feedback submission with validation, duplicate protection, and rate limiting.
    • Added source review workflows for recording document decisions and evidence.
    • Added interaction IDs to answer and streamed-answer responses for tracking.
    • Updated user feedback confirmations and handling for demo answers.
  • Documentation

    • Documented the new feedback and source review API routes.
  • Tests

    • Added coverage for feedback persistence and source review validation.

@supabase

supabase Bot commented Jul 13, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@BigSimmo, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 8 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f4cfef00-c8a4-4ec5-bea5-83ee46acfe4b

📥 Commits

Reviewing files that changed from the base of the PR and between 3891152 and be0b3eb.

📒 Files selected for processing (6)
  • src/app/api/answer-feedback/route.ts
  • src/app/api/documents/[id]/reviews/route.ts
  • src/components/ClinicalDashboard.tsx
  • src/lib/source-review.ts
  • tests/answer-feedback-route.test.ts
  • tests/source-review-route.test.ts
📝 Walkthrough

Walkthrough

Adds APIs for recording answer feedback and document source reviews, with validation, rate limiting, Supabase persistence, cache invalidation, tests, sitemap entries, and interaction IDs in answer responses.

Changes

Feedback and source review APIs

Layer / File(s) Summary
Persistence and validation contracts
src/lib/source-review.ts, src/lib/api-rate-limit.ts, src/lib/supabase/database.types.ts, src/lib/types.ts
Adds source review decision types, new rate-limit buckets, Supabase table/RPC typings, and an optional answer interaction identifier.
Source review submission flow
src/app/api/documents/[id]/reviews/route.ts, tests/source-review-route.test.ts, docs/site-map.md
Adds validated source review recording through record_source_review, cache invalidation, error handling, tests, and sitemap documentation.
Answer feedback submission flow
src/app/api/answer-feedback/route.ts, src/components/ClinicalDashboard.tsx, tests/answer-feedback-route.test.ts, docs/site-map.md
Adds hashed answer feedback submission, rate limiting, de-duplicated persistence, dashboard integration, success/error handling, tests, and sitemap documentation.
Interaction identifier propagation
src/app/api/answer/route.ts, src/app/api/answer/stream/route.ts
Adds per-request interaction IDs to JSON and final SSE responses across normal, refusal, empty-scope, demo, and fallback paths.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant SourceReviewRoute
  participant Supabase
  participant RAGCache
  Client->>SourceReviewRoute: Submit source review
  SourceReviewRoute->>Supabase: Record review via RPC
  Supabase-->>SourceReviewRoute: Return recorded review
  SourceReviewRoute->>RAGCache: Invalidate owner cache
  SourceReviewRoute-->>Client: Return 201 response
Loading
sequenceDiagram
  participant ClinicalDashboard
  participant AnswerFeedbackRoute
  participant Supabase
  ClinicalDashboard->>AnswerFeedbackRoute: Submit interaction and hashed answer feedback
  AnswerFeedbackRoute->>Supabase: Insert feedback row
  Supabase-->>AnswerFeedbackRoute: Return insertion result
  AnswerFeedbackRoute-->>ClinicalDashboard: Return success or error response
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: adding privacy-minimized clinical feedback recovery.
Description check ✅ Passed The PR description is detailed and covers summary, verification, and governance, but it doesn't follow the exact template sections or checklist format.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/cleanup-domain1-governance

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3891152cc5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/app/api/documents/[id]/reviews/route.ts
@github-actions

Copy link
Copy Markdown

@codex resolve actionable Codex review findings for this pull request and current head using the repository instructions. This is the pull request's single automatic repair pass: do not perform a fresh review, create new standalone findings, or request another review. Work only the existing unresolved Codex threads on the current head. Always fix P0 and P1 findings. For P2 and lower findings, fix only clear, scoped, low-risk issues; otherwise disposition them with a concise reason. After fixing or dispositioning a thread, reply in that thread with as the first line, followed by a concise summary; that marker authorizes the workflow to close that exact thread. If human input or new authorization is required, do not use the marker and leave the thread open with the blocker. Finish only after every actionable thread is fixed or dispositioned and closed, or explicitly left open for a human decision. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/components/ClinicalDashboard.tsx (1)

2573-2589: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Use authBoundFetch instead of plain fetch for consistency and cancellation.

Every other mutation in this file (mutateDocumentLabel, retryJob, reindexDocument) goes through authBoundFetch, which ties the request to an AbortController and validates the auth epoch before committing state. This call uses plain fetch with no abort signal, so a hung request leaves pendingFeedback set indefinitely (buttons stuck disabled) with no way to cancel on unmount or a superseding auth event.

♻️ Proposed fix
-      const response = await fetch("/api/answer-feedback", {
-        method: "POST",
-        headers: {
-          "Content-Type": "application/json",
-          ...authorizationHeader,
-        },
-        body: JSON.stringify({
-          interactionId: answer.interactionId,
-          feedbackCategory: feedbackType,
-          answerHash,
-          sourceIds: sourceChunkIds,
-          citedSourceIds: citedChunkIds,
-          route: answer.routingMode ?? null,
-          model: answer.modelUsed ?? null,
-          providerRequestIds: answer.openAIRequestIds ?? [],
-        }),
-      });
+      const { response, requestEpoch } = await authBoundFetch("/api/answer-feedback", {
+        method: "POST",
+        headers: {
+          "Content-Type": "application/json",
+          ...authorizationHeader,
+        },
+        body: JSON.stringify({
+          interactionId: answer.interactionId,
+          feedbackCategory: feedbackType,
+          answerHash,
+          sourceIds: sourceChunkIds,
+          citedSourceIds: citedChunkIds,
+          route: answer.routingMode ?? null,
+          model: answer.modelUsed ?? null,
+          providerRequestIds: answer.openAIRequestIds ?? [],
+        }),
+      });
+      if (!isAuthEpochCurrent(requestEpoch)) return;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ClinicalDashboard.tsx` around lines 2573 - 2589, Replace the
plain fetch call in the answer-feedback mutation with the file’s existing
authBoundFetch helper, passing the same POST options and request body while
preserving the authorization headers. Ensure the mutation uses the helper’s
cancellation and auth-epoch behavior so pendingFeedback can be cleared when the
request is aborted or superseded.
tests/answer-feedback-route.test.ts (1)

11-56: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider covering the duplicate/rate-limited/demo-mode branches too.

Only the happy path is tested. The 409 duplicate-detection branch, 429 rate-limited branch, and demo-mode 400 rejection in route.ts are untested, and the duplicate branch is exactly what's flagged as fragile in src/app/api/answer-feedback/route.ts.

As per path instructions, "For high-risk code involving authentication, authorization, private data, database access, clinical output, ingestion, provider calls, billing, uploads, or background jobs, review carefully for regressions, missing tests, rollback safety, and conservative failure behavior."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/answer-feedback-route.test.ts` around lines 11 - 56, Expand the “answer
feedback route” tests to cover the 409 duplicate-detection, 429 rate-limit, and
400 demo-mode rejection branches in POST. Reconfigure the existing mocks to
trigger each branch, assert the corresponding response status, and verify
duplicate handling remains covered as implemented in route.ts.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/api/answer-feedback/route.ts`:
- Around line 66-70: Update the error handling in the answer feedback insertion
flow to detect duplicate submissions by checking error.code for the stable
unique-violation code 23505 instead of matching error.message with a regex.
Preserve the existing 409 response for that code and the PublicApiError path for
other insertion failures.
- Around line 17-20: Update the answer-feedback handler around bodySchema and
its persistence flow to verify interactionId references an existing answer
before storing feedback. Add a server-side answer lookup or ledger relationship
check after UUID validation, reject fabricated or nonexistent IDs, and preserve
the existing uniqueness validation for legitimate feedback.

In `@src/app/api/documents/`[id]/reviews/route.ts:
- Around line 21-26: Update the reviewDate validation schema to parse valid
calendar dates and reject dates after the current date in the RPC’s
Australia/Perth timezone, while preserving nullable and optional behavior.
Locate the route’s RPC invocation to ensure the same date basis is used
consistently, and remove the future-date fixture from source-review-route tests.

In `@src/components/ClinicalDashboard.tsx`:
- Line 2587: Update the providerRequestIds assignment in the answer-feedback
submission to send at most 10 IDs from answer.openAIRequestIds, preserving the
existing empty-array fallback when no IDs are present.

---

Nitpick comments:
In `@src/components/ClinicalDashboard.tsx`:
- Around line 2573-2589: Replace the plain fetch call in the answer-feedback
mutation with the file’s existing authBoundFetch helper, passing the same POST
options and request body while preserving the authorization headers. Ensure the
mutation uses the helper’s cancellation and auth-epoch behavior so
pendingFeedback can be cleared when the request is aborted or superseded.

In `@tests/answer-feedback-route.test.ts`:
- Around line 11-56: Expand the “answer feedback route” tests to cover the 409
duplicate-detection, 429 rate-limit, and 400 demo-mode rejection branches in
POST. Reconfigure the existing mocks to trigger each branch, assert the
corresponding response status, and verify duplicate handling remains covered as
implemented in route.ts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: aa57e9ca-d98c-4877-bc7c-5cf630f4536f

📥 Commits

Reviewing files that changed from the base of the PR and between d3c8b30 and 3891152.

📒 Files selected for processing (12)
  • docs/site-map.md
  • src/app/api/answer-feedback/route.ts
  • src/app/api/answer/route.ts
  • src/app/api/answer/stream/route.ts
  • src/app/api/documents/[id]/reviews/route.ts
  • src/components/ClinicalDashboard.tsx
  • src/lib/api-rate-limit.ts
  • src/lib/source-review.ts
  • src/lib/supabase/database.types.ts
  • src/lib/types.ts
  • tests/answer-feedback-route.test.ts
  • tests/source-review-route.test.ts

Comment thread src/app/api/answer-feedback/route.ts
Comment thread src/app/api/answer-feedback/route.ts
Comment thread src/app/api/documents/[id]/reviews/route.ts
Comment thread src/components/ClinicalDashboard.tsx Outdated
@BigSimmo
BigSimmo enabled auto-merge (squash) July 13, 2026 16:07
@BigSimmo
BigSimmo merged commit 622988f into main Jul 13, 2026
24 checks passed
@BigSimmo
BigSimmo deleted the codex/cleanup-domain1-governance branch July 13, 2026 16:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant